/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#include "Thread.h"
#include "Tracer.h"
#include <time.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <signal.h>

#define PTHREAD_ATTR_ENABLE     0   /* 是否使能线程属性控制 */
#define THREAD_COUNT_DEFAULT    5   /* 默认线程池线程个数 */
namespace libemb{

/**
 *  \brief    线程属性
 *  policy    可取值:SCHED_POLICY_FIFO(优先级1~99),SCHED_POLICY_RR(优先级1~99),SCHED_POLICY_OTHER(优先级固定为0)
 *  priority  可取值:1~99,数字越大，优先级越高
 *  stackSize 最小值为PTHREAD_STACK_MIN(16384)
 *  inherit   是否继承父线程调度策略:
 *            PTHREAD_INHERIT_SCHED(继承自父线程,忽略当前设置的属性)
 *            PTHREAD_EXPLICIT_SCHED(采用当前设置的线程属性))
 */
ThreadAttribute::ThreadAttribute()
{
	m_policy = SCHED_POLICY_OTHER;
	m_priority = 0;
	m_inherit = PTHREAD_INHERIT_SCHED;
	m_stackSize = PTHREAD_STACK_MIN;
}

ThreadAttribute::~ThreadAttribute()
{
}

int ThreadAttribute::policy()
{
	return m_policy;
}
int ThreadAttribute::priority()
{
	return m_priority;
}
int ThreadAttribute::inherit()
{
	return m_inherit;
}
int ThreadAttribute::stackSize()
{
	return m_stackSize;
}

Runnable::Runnable():
m_isStarted(false)
{
}

Runnable::~Runnable()
{
}

bool Runnable::isStarted()
{
    return m_isStarted;
}

/**
 *  \brief  检查线程取消
 *  \param  void
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool Runnable::checkThreadCancel()
{
    #ifdef OS_ANDROID
    //TRACE_ERR_CLASS("Not support thread cancel!\n");
    return false;
    #else
    pthread_testcancel();
    return true;
    #endif
}

Thread::Thread()
{
	m_pRunnableTarget = NULL;
	m_threadStatus = THREAD_STATUS_NEW;
	m_threadID = 0;
	m_isSetAttribute = false;
}

Thread::~Thread()
{
	
}

/**
 *  \class  TaskInfo_S
 *  \brief  任务参数结构体 
 */
typedef struct{
    Runnable* m_owner;
    SEL_Runnable_taskMain m_task;
    void* m_arg;
}TaskInfo_S;

/**
 *  \brief  创建任务(子线程)
 *  \param  pRunnable Runnable对象
 *  \param  task 任务方法
 *  \return 成功返回STATUS_OK,失败返回STATUS_ERROR
 *  \note   none
 */
int Thread::createTask(Runnable* pRunnable,SEL_Runnable_taskMain task,void* taskArg)
{
    pthread_t threadID;
    /* 一定要使用malloc分配arg内存,等线程跑起来后再free;
     * 千万不能直接定义一个临时变量,否则会出现段错误;
     * 原因是:pthread_create返回时线程实际还没运行,
     * 而返回后此函数退出,定义的变量已经失效,因此出错!
     */
    TaskInfo_S *taskInfo = (TaskInfo_S*)malloc(sizeof(TaskInfo_S));
    taskInfo->m_owner=pRunnable;
    taskInfo->m_task=task;
    taskInfo->m_arg=taskArg;
	if(NULL==pRunnable || task == NULL)
	{
	    TRACE_ERR("Thread::createTask,parameter error!\n");
		return STATUS_ERROR;
	}
	if(0!=pthread_create(&threadID, NULL, taskEntry, taskInfo))
	{
        TRACE_ERR("Thread::createTask,create task thread failed:%s!\n", ERROR_STRING); 
		return STATUS_ERROR;
	}
	return STATUS_OK;   
}

/**
 *  \brief  初始化实时抢占系统
 *  \param  void
 *  \return 成功返回true,失败返回false
 *  \note   此函数仅限在linux-rt上使用
 */
bool Thread::initWithPreemptRT()
{
	/* 锁定当前进程内存,防止被系统SWAP到硬盘中去,影响实时性能 */
	if(mlockall(MCL_CURRENT|MCL_FUTURE) == -1) 
	{
		return false;
	}

	#if 0
    {
    	/* 栈空间预留,这样做的目的是什么呢??? 
    	 * 故意让栈空间往下生长8K,其后的函数调用和局部变量将不再导致
    	 * 栈增长(依赖于page fault和内存申请) 
    	 */
    	unsigned char dummy[8096];
    	memset(dummy, 0, 8096);
	}
	#endif
	return true;
}


/**
 *  \brief  微秒延时函数
 *  \param  us 要延时的微秒数
 *  \return void
 *  \note   函数休眠时,当前进程可能会让出CPU,引发进程调度
 *          注意使用usleep时,时间不能设置得太短,否则调度时
 *          进程间切换太频繁非常耗资源!!!推荐最小值为100us
 */
void Thread::usleep(int us)
{
	/* 注意:sleep和select等带有睡眠阻塞性质的函数一般都可能会被信号打断,此时会立即返回,达不到睡眠的效果 */
	#if 0
	/* 一般情况下不需要十分精确的定时,所以这里利用select超时机制来定时 */
	struct timeval tv;
	if (us<=0) 
	{
		us = 1;
	}

	tv.tv_sec = us/1000000;
	if (tv.tv_sec==0)
	{
		tv.tv_usec = us;
	}
	else
    {
    	tv.tv_usec = us%1000000;
	}
	select(0,NULL,NULL,NULL,&tv);
	#else
	struct timespec ts;
	if (us<=0) 
	{
		us = 1;
	}
    ts.tv_sec = us/1000000;
	if (ts.tv_sec==0)
	{
		ts.tv_nsec = us*1000;
	}
    else
    {
    	ts.tv_nsec = (us%1000000)*1000;
    }
	clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);/* 使用MONITONIC时钟,不受更改系统时钟的影响 */
	#endif
}

/**
 *  \brief  毫秒延时函数
 *  \param  ms 要延时的毫秒数
 *  \return void
 *  \note   函数休眠时,当前进程可能会让出CPU,引发进程调度
 */
void Thread::msleep(int ms)
{
	Thread::usleep(ms*1000);
}


void Thread::setAttribute(int policy, int priority, bool inherit,int stackSize)
{
	m_threadAttribute.m_policy = policy;
	if (policy==SCHED_POLICY_OTHER)
	{
		priority = 0;
	}
	m_threadAttribute.m_priority = priority;
	m_threadAttribute.m_stackSize = stackSize;
	m_threadAttribute.m_inherit = inherit?PTHREAD_INHERIT_SCHED:PTHREAD_EXPLICIT_SCHED;
	if (stackSize<PTHREAD_STACK_MIN)
	{
		m_threadAttribute.m_stackSize = PTHREAD_STACK_MIN;
	}
	else
	{
		m_threadAttribute.m_stackSize = stackSize;
	}
	m_isSetAttribute = true;
}

/**
 *  \brief  启动线程
 *  \param  pRunnable Runnable对象
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool Thread::start(Runnable* pRunnable)
{
	pthread_attr_t* pAttr = NULL;
	if(NULL==pRunnable || m_threadID != 0)
	{
		return false;
	}
    
	m_pRunnableTarget = pRunnable;
	/* 设置线程属性 */
	pthread_attr_init(&m_attribute);
	if(pthreadAttrSet(&m_attribute))
	{
		pAttr = &m_attribute;
	}
	if(0!=pthread_create(&m_threadID, pAttr, threadEntry, this))
	{
        TRACE_ERR_CLASS("%s create thread failed:%s!\n", CLASS_NAME(m_pRunnableTarget),ERROR_STRING); 
		return false;
	}
	m_threadStatus = THREAD_STATUS_START;
    //TRACE_REL_CLASS("%s create thread(%u) ok.\n",CLASS_NAME(m_pRunnableTarget),m_threadID); 
    pthread_attr_destroy(&m_attribute);
	return true;
}
/**
 *  \brief  等待线程退出
 *  \param  none
 *  \return none
 *  \note   none
 */
void Thread::join()
{
    while (m_threadStatus!=THREAD_STATUS_EXIT) 
    {
        msleep(10);
    }
}

/**
 *  \brief  取消线程
 *  \param  void
 *  \return void
 *  \note   
 */
bool Thread::cancel()
{
#ifdef OS_ANDROID
	return false;
#else
    if (m_threadID > 0)
    {
        /* 执行pthread_cancel后,并不会直接取消线程,必须等到下一次系统调用或者pthread_testcancel才会真正取消线程 */
    	if (0!=pthread_cancel(m_threadID))
    	{
            TRACE_ERR_CLASS("pthread cancel error:%s.\n", ERROR_STRING); 
    		return false;
    	}
        pthread_join(m_threadID, NULL);
    }
	m_threadStatus = THREAD_STATUS_EXIT;
    m_threadID = 0;
    return true;
#endif
}

/**
 *  \brief  判断线程是否正在运行
 *  \param  void
 *  \return 正在运行返回true,否则返回false
 *  \note   none
 */
bool Thread::isRunning()
{
	if(m_threadStatus == THREAD_STATUS_RUNNING)
	{
		return true;
	}
	return false;
}
/**
 *  \brief  获取线程当前状态
 *  \param  void
 *  \return int  线程状态(THREAD_STATUS_E)
 *  \note   none
 */
int Thread::getStatus()
{
    return m_threadStatus;
}

bool Thread::pthreadAttrSet(pthread_attr_t* pAttr)
{
	if (pAttr==NULL)
	{
		return false;
	}
	struct sched_param param;
	if(pthread_attr_setstacksize(pAttr, m_threadAttribute.m_stackSize)!=0)
    {
        TRACE_ERR_CLASS("Set thread attribute stack size:%d failed!\n",m_threadAttribute.m_stackSize);
		return false;
    }
	
	/* 设置调度策略 */
    if (pthread_attr_setschedpolicy(pAttr, m_threadAttribute.m_policy)!=0)
	{
		TRACE_ERR_CLASS("Set thread attribute policy:%d failed\n",m_threadAttribute.m_policy);
		return false;
    }
	
	/* 设置调度优先级 */
    param.sched_priority = m_threadAttribute.m_priority;
    if (pthread_attr_setschedparam(pAttr, &param)!=0)
    {
		TRACE_ERR_CLASS("Set thread attribute priority:%d failed\n",m_threadAttribute.m_priority);
		return false;
    }

	/* 设置线程调度策略继承属性 */
	if (pthread_attr_setinheritsched(pAttr, m_threadAttribute.m_inherit)!=0)
	{
		TRACE_ERR_CLASS("Set thread sched inherit:%d failed\n",m_threadAttribute.m_inherit);
		return false;
    }
	return true;
}


/* 线程运行函数 */
void Thread::threadMain()
{
    //TRACE_DBG("---threadMain enter---\n");
	m_threadStatus = THREAD_STATUS_RUNNING;
	m_threadID = pthread_self();
	if(m_pRunnableTarget != NULL)
	{
		m_pRunnableTarget->run();
	}
	m_threadStatus = THREAD_STATUS_EXIT;
    //TRACE_DBG("---threadMain exit---%d\n",m_pRunnableTarget);
	pthread_exit(NULL);
    m_threadID = 0;
}

/* 线程入口点 */
void * Thread::threadEntry(void *arg)
{
    //TRACE_DBG("---threadEntry enter---\n");
    pthread_detach(pthread_self());
    /* 设置detach属性,在线程结束后自动回收资源 */
	Thread* pThread = (Thread*)arg;
	if(pThread != NULL)
	{
		pThread->threadMain();
	}
    //TRACE_DBG("---threadEntry exit---%d\n",pThread);
	return (void*)0;
}

/* 任务入口点 */
void * Thread::taskEntry(void *arg)
{
    pthread_detach(pthread_self());
	TaskInfo_S* pTask = (TaskInfo_S*)arg;
	if(pTask->m_owner!=NULL && pTask->m_task!=NULL)
	{
		((pTask->m_owner)->*(pTask->m_task))(pTask->m_arg);
	}
    free(pTask);
    pTask = NULL;
	return (void*)0;
}

/**
 *  \brief  线程池构造函数
 *  \param  none
 *  \return none
 */
ThreadPool::ThreadPool():
m_initFlag(false),
m_maxThreadCount(0),
m_usedThreadCount(0)
{
}
/**
 *  \brief  线程池析构函数
 *  \param  none
 *  \return none
 */
ThreadPool::~ThreadPool()
{
}
/**
 *  \class  ThreadInfo_S
 *  \brief  线程参数结构体 
 */
typedef struct{
    ThreadPool* m_pool;
    int m_index;
}ThreadInfo_S;

/**
 *  \brief  线程池初始化
 *  \param  maxThreadCount 最大线程个数
 *  \return 成功返回true,失败返回false
 */
bool ThreadPool::init(int maxThreadCount)
{
    if (m_initFlag)
    {
        return false;
    }
    if (maxThreadCount<=0)
    {
       maxThreadCount = THREAD_COUNT_DEFAULT;
    }
    m_maxThreadCount=0;
    m_threadVect.clear();
    for(int i=0; i<maxThreadCount; i++)
    {
        pthread_t threadID;
        ThreadInfo_S *threadInfo = (ThreadInfo_S*)malloc(sizeof(ThreadInfo_S));
        threadInfo->m_pool=this;
        threadInfo->m_index=i; 
    	if(0!=pthread_create(&threadID, NULL, threadEntry, threadInfo))
    	{
            TRACE_ERR("ThreadPool::createThread,create thread failed:%s!\n", ERROR_STRING); 
    		return false;
    	}
        else
        {
            m_threadVect.push_back(NULL);
            m_maxThreadCount++;
        }
    }
    m_initFlag=true;
    return true;
}
/**
 *  \brief  启动线程
 *  \param  runnable 线程运行体
 *  \param  priority 线程运行优先级
 *  \return 成功返回true,失败返回false
 */
bool ThreadPool::start(Runnable* runnable, int priority)
{
    if (runnable==NULL)
    {
        return false;
    }
    AutoLock lock(&m_vectLock);
    if (m_usedThreadCount>=m_maxThreadCount)
    {
        return false;
    }
    for(int i=0; i<m_maxThreadCount; i++)
    {
        if (m_threadVect[i]==NULL)
        {
			runnable->m_isStarted=true;
            m_threadVect[i]=runnable;
	        return true; 
        }
    }
    return false; 
}
/**
 *  \brief  停止线程
 *  \param  runnable 线程运行体
 *  \return 成功返回true,失败返回false
 */
bool ThreadPool::cancel(Runnable* runnable)
{
    if (runnable==NULL)
    {
        return true;
    }
    AutoLock lock(&m_vectLock);
    for(int i=0; i<m_maxThreadCount; i++)
    {
        if (m_threadVect[i]==runnable)
        {
            m_threadVect[i]->m_isStarted=false;
            m_threadVect[i]=NULL;
            return true;
        }
    }
    return false;
}
/**
 *  \brief  返回当前线程池最大线程个数
 *  \param  none
 *  \return 线程个数
 */
int ThreadPool::maxThreadCount()
{
    return m_maxThreadCount;
}
/**
 *  \brief  返回当前可用线程个数
 *  \param  none
 *  \return 线程个数
 */
int ThreadPool::idleThreadCount()
{
    return m_maxThreadCount-m_usedThreadCount;
}

void ThreadPool::startThread(int index)
{
    if (index<m_maxThreadCount)
    {
        m_vectLock.lock();
        Runnable* runnable = m_threadVect[index];
        if (runnable!=NULL)
        {
            m_usedThreadCount++;
            m_vectLock.unLock();
            runnable->run();
            /* 线程使用完毕,正常退出 */
            runnable->m_isStarted=false;
   			m_vectLock.lock();
            m_threadVect[index]=NULL;
            m_usedThreadCount--;
        }
        m_vectLock.unLock();        
    }
}
/* 线程入口点 */
void * ThreadPool::threadEntry(void *arg)
{
    pthread_detach(pthread_self());
    ThreadInfo_S* threadInfo = (ThreadInfo_S*)arg;
    ThreadPool* threadPool = threadInfo->m_pool;
    int threadIndex = threadInfo->m_index;
	if(threadPool!=NULL && threadIndex>=0)
	{
	    while(1)
	    {
	        threadPool->startThread(threadIndex);
            Thread::msleep(10);
        }
	}
    free(threadInfo);
    threadInfo = NULL;
	return (void*)0;
}

}

